#ifndef __CUI_H__
#define __CUI_H__
// https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/index.html
// https://invisible-island.net/ncurses/man/
#include <ncurses/ncurses.h>
#include <ncurses/menu.h>
#include <ncurses/panel.h>
#include <ncurses/form.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>

#include "include/compiler.h"
#include "include/list.h"
#include "include/circ_buf.h"
#include "cJSON.h"

#define T0022_BANNER_VERSION		"dxtviewer v1.00"
/*
struct l0032_xtable_valueenum {
	char valdesc[L0032_DXT_DESC_MAX_LEN];
	int  val;
};

struct l0032_xtable_namedpoint {
	char name[L0032_DXT_NAME_MAX_LEN];
	char desc[L0032_DXT_DESC_MAX_LEN];
	int nvalues;
	struct l0032_xtable_valueenum *valueenum;
	uint32_t flags;
};

struct l0032_xtable_entry {
	entry_handle hentry;
	char name[L0032_DXT_NAME_MAX_LEN];
	char desc[L0032_DXT_DESC_MAX_LEN];
	uint32_t flags;
	int npoints;
	struct l0032_xtable_namedpoint *namedpoints;	// for POINT_ENUM type usage, otherwise is NULL
};

struct l0032_xtable {
	size_t sz;
	char appname[L0032_DXT_NAME_MAX_LEN];
	char parselib[L0032_DXT_NAME_MAX_LEN];

	int nentries;
	int nrefentries;

	struct l0032_xtable_entry *entries;
	struct l0032_xtable_entry *refentries;
};
*/


/*
TODO: 
1. widget destroy的调用层次比较乱，应该实现递归销毁
2. widget->ops不能释放 因为是共用的，但ops有些地方又是动态分配的，导致widget释放的时候内存泄漏, widget->ops改成静态的，不要动态分配	X
3. clabel放到widget里似乎不太合适
 */

/*
/opt/deserted/aarch64-linux-gnu/include/ncurses/ncurses.h
#define COLOR_BLACK     0
#define COLOR_RED       1
#define COLOR_GREEN     2
#define COLOR_YELLOW    3
#define COLOR_BLUE      4
#define COLOR_MAGENTA   5
#define COLOR_CYAN      6
#define COLOR_WHITE     7
*/

/* color base */
#define REDBLK			1	//front color, back color
#define GRNBLK			2
#define BLUBLK			3
#define CYNBLK			4
#define YELBLK			5
#define WHTBLK			6

/* reverse color */
#define BLKRED			7
#define BLKGRN			8
#define BLKBLU			9
#define BLKCYN			10
#define BLKYEL			11
#define BLKWHT			12

#define COLOR_BLACK_STR			"black"
#define COLOR_WHITE_STR			"white"
#define COLOR_RED_STR			"red"
#define COLOR_GREEN_STR			"green"
#define COLOR_BLUE_STR			"blue"
#define COLOR_CYAN_STR			"cyan"
#define COLOR_YELLOW_STR		"yellow"


/* keys ncurses not interpreted */
#define KEY_ENTER_US		10
#define KEY_BACKSPACE_US		8
#define KEY_SPACE			32
#define KEY_TAB				9
#define KEY_ESC				27

#define CUI_MENUITEMS_MAX				32
#define CUI_FIELDITEMS_MAX				32
#define CUI_SLOTNAME_BUFLEN				32
#define CUI_WIDGETTITLE_BUFLEN			32
#define CUI_WIDGETMARK_BUFLEN			8

#define CUI_WIDGET_TITLESPLITTER_COLOFFSET	2
#define CUI_WIDGET_TITLESPLITTER_WIDTH		1
#define CUI_WIDGET_TITLE_HEIGHT				1
#define CUI_WIDGET_BORDER_WIDTH				1
#define CUI_WIDGET_BORDER_WIDTHX2			(CUI_WIDGET_BORDER_WIDTH * 2)
#define CUI_WIDGET_BORDER_MARGIN			1

/* inertial properties */
#define CUI_WIDGET_FLAGS_NONE				0x000
#define CUI_WIDGET_FLAGS_BORDER				0x001
#define CUI_WIDGET_FLAGS_TITLE				0x002
#define CUI_WIDGET_FLAGS_INPUTEVENT			0x004		// widget has input interface which eventloop matters
#define CUI_WIDGET_FLAGS_HIGHLIGHT			0x008
#define CUI_WIDGET_FLAGS_TEMPLATE			0x010
#define CUI_WIDGET_FLAGS_KEYEVENTOFF		0x020
/* widget type */
#define CUI_WIDGET_FLAGS_WINDOW				0x100
#define CUI_WIDGET_FLAGS_MENU				0x200
#define CUI_WIDGET_FLAGS_FORM				0x400
#define CUI_WIDGET_FLAGS_PANEL				0x800

/* cursor visibility */
#define CUI_CURSOR_INVISIBLE				0
#define CUI_CURSOR_NORMAL					1
#define CUI_CURSOR_VERYVISIBLE				2


#define IS_WIDGET_BORDER(WIDGET)			((WIDGET)->flags & CUI_WIDGET_FLAGS_BORDER)
#define IS_WIDGET_TITLE(WIDGET)				((WIDGET)->flags & CUI_WIDGET_FLAGS_TITLE)
#define IS_WIDGET_INPUTEVENT(WIDGET)		((WIDGET)->flags & CUI_WIDGET_FLAGS_INPUTEVENT)

#define SET_WIDGET_TYPE(WIDGET, TYPE)		((WIDGET)->flags |= CUI_WIDGET_FLAGS_##TYPE)
#define IS_WIDGET_TYPE(WIDGET, TYPE)		((WIDGET)->flags & CUI_WIDGET_FLAGS_##TYPE)

#define SET_WIDGET_TEMPLATE(WIDGET) \
do {\
	if (WIDGET != NULL) {\
		(WIDGET)->flags |= CUI_WIDGET_FLAGS_TEMPLATE;\
	}\
} while(0)
#define UNSET_WIDGET_TEMPLATE(WIDGET) \
do {\
	if ((WIDGET != NULL)) {\
		(WIDGET)->flags &= ~CUI_WIDGET_FLAGS_TEMPLATE;\
	}\
} while(0)
#define IS_WIDGET_TEMPLATE(WIDGET)	((WIDGET)->flags & CUI_WIDGET_FLAGS_TEMPLATE)

#define SET_WIDGET_HIGHLIGHT(WIDGET) \
do {\
	if (WIDGET != NULL) {\
		(WIDGET)->flags |= CUI_WIDGET_FLAGS_HIGHLIGHT;\
	}\
} while(0)
#define UNSET_WIDGET_HIGHLIGHT(WIDGET) \
do {\
	if ((WIDGET != NULL)) {\
		(WIDGET)->flags &= ~CUI_WIDGET_FLAGS_HIGHLIGHT;\
	}\
} while(0)
#define IS_WIDGET_HIGHLIGHT(WIDGET)	((WIDGET)->flags & CUI_WIDGET_FLAGS_HIGHLIGHT)

#define SET_WIDGET_KEYEVENTOFF(WIDGET) \
do {\
	if (WIDGET != NULL) {\
		(WIDGET)->flags |= CUI_WIDGET_FLAGS_KEYEVENTOFF;\
	}\
} while(0)
#define UNSET_WIDGET_KEYEVENTOFF(WIDGET) \
do {\
	if ((WIDGET != NULL)) {\
		(WIDGET)->flags &= ~CUI_WIDGET_FLAGS_KEYEVENTOFF;\
	}\
} while(0)
#define IS_WIDGET_KEYEVENTOFF(WIDGET)	((WIDGET)->flags & CUI_WIDGET_FLAGS_KEYEVENTOFF)

#define CUI_WIDGET_OPS_RECURSIVE_TYPE_CREATE						0
#define CUI_WIDGET_OPS_RECURSIVE_TYPE_PAINT							1
#define CUI_WIDGET_OPS_RECURSIVE_TYPE_DESTROY						2
#define CUI_WIDGET_OPS_RECURSIVE_TYPE_KEYEVENT						3
#define CUI_WIDGET_OPS_RECURSIVE_TYPE_MOUSEEVENT					4
#define CUI_WIDGET_OPS_RECURSIVE_TYPE_CUSTOMIZED_INITIALIZE			5

#define CUI_CONFIELD_VERSION                "version"
#define CUI_CONFIELD_ID 					"id"
#define CUI_CONFIELD_WINDOWLIST				"windowlist"
#define CUI_CONFIELD_WIDGETLIST             "widgetlist"
#define CUI_CONFIELD_ENABLE                 "enable"
#define CUI_CONFIELD_WINDOW                 "window"
#define CUI_CONFIELD_PANEL					"panel"
#define CUI_CONFIELD_PANELCOUNT				"panel-count"
#define CUI_CONFIELD_ATTRIBUTES             "attributes"
#define CUI_CONFIELD_LAYOUTGROUPLIST		"layoutgrouplist"
#define CUI_CONFIELD_LAYOUTGROUP			"layoutgroup"
#define CUI_CONFIELD_LAYOUT					"layout"
#define CUI_CONFIELD_SIZE                   "size"
#define CUI_CONFIELD_LINES                  "lines"
#define CUI_CONFIELD_COLS                   "cols"
#define CUI_CONFIELD_BEGINX                 "beginx"
#define CUI_CONFIELD_BEGINY                 "beginy"
#define CUI_CONFIELD_BORDER                 "border"
#define CUI_CONFIELD_MENU                   "menu"
#define CUI_CONFIELD_ITEMLIST               "itemlist"
#define CUI_CONFIELD_FORMAT					"format"
#define CUI_CONFIELD_MARK					"mark"
#define CUI_CONFIELD_MENUROWS				"menu-rows"
#define CUI_CONFIELD_MENUCOLS				"menu-cols"
#define CUI_CONFIELD_NAME					"name"
#define CUI_CONFIELD_DESC					"desc"
#define CUI_CONFIELD_ACTIONLIST				"actionlist"
#define CUI_CONFIELD_EVENT					"event"
#define CUI_CONFIELD_SLOT					"slot"
#define CUI_CONFIELD_TITLE					"title"
#define CUI_CONFIELD_KEYEVENT				"keyevent"
#define CUI_CONFIELD_MOUSEEVENT				"mouseevent"
#define CUI_CONFIELD_LABEL					"label"
#define CUI_CONFIELD_FORECOLOR				"forecolor"
#define CUI_CONFIELD_BACKCOLOR				"backcolor"
#define CUI_CONFIELD_TEXT					"text"
#define CUI_CONFIELD_FORM					"form"
#define CUI_CONFIELD_FIELDS					"fields"
#define CUI_CONFIELD_OPTIONS				"options"

#define CUI_CONFIELD_BACKGROUND_UNDERLINE				"UNDERLINE"
#define CUI_CONFIELD_OPTION_AUTOSKIP					"AUTOSKIP"


#define CUI_CONFIG_FIELDVALUE_TRUE             1
#define CUI_CONFIELD_VERSION_VALUE          1

#define CUI_WIDGETEVENT_CALLBACK_KEYEVENT		0

#define CUI_WIDGETEVENT_PROPAGATION				0
#define CUI_WIDGETEVENT_HANDLED					1

#define EXPORT_WIDGET_INITIALIZE(FUNC)	\
int __##FUNC##_export(struct cui_widget_st* widget) {ERRSYS_INFOPRINT("%s\n", __func__);return FUNC(widget);}
#define GET_EXPORT_WIDGET_INITIALIZE_STRING(WIDGET_NAME, FUNCNAME_BUF, FUNCNAME_BUFSIZE) \
do {\
	snprintf((FUNCNAME_BUF), (FUNCNAME_BUFSIZE), "__%s_initialize_export", (WIDGET_NAME));\
} while(0)

#define PANEL_REARRANGE_ROW               0
#define PANEL_REARRANGE_COLUMN            1
#define PANEL_REARRANGE_GRID              2

struct cui_widget_event_callback;
struct cui_widget_st;
// extern struct cui_widget_operations __dummy_ops;

struct cui_widget_border_st {
	chtype ls, rs, ts, bs, tl, tr, bl, br;
};

struct cui_widget_hline_st {
	chtype lt, rt;
};

#define CUI_SCRSTACK_SIZE		32

struct cui_screen_stack {
	struct cui_widget_st *widgets[CUI_SCRSTACK_SIZE];
	int count;
};

struct cui_screen_st {
	struct cui_widget_st *inputwidget; // current input widget
	struct cui_screen_stack stack;
	struct list_head windowlist;
	struct list_head layoutgrouplist;		// one widget has multiple layout groups
	int refresh;
	int cursor_visibility;
};

#define CUI_EVENTCB_TYPE_KEYEVENT		0
#define CUI_EVENTCB_TYPE_MOUSEEVENT		1
#define CUI_EVENTCB_TYPE_PAINT			2

typedef int (*CUI_EVENT_CALLBACK)(struct cui_widget_st*, int);

struct cui_widget_event_callback {
	int (*keyevent)(struct cui_widget_st*, int key);
	int (*mouseevent)(struct cui_widget_st*, int key);
	int (*paint)(struct cui_widget_st*, int dummy);
};
#define LAYOUT_FLAGS_BEGINX_PERCENT		1
#define LAYOUT_FLAGS_BEGINY_PERCENT		2
#define LAYOUT_FLAGS_COLS_PERCENT		4
#define LAYOUT_FLAGS_LINES_PERCENT		8
#define IS_LAYOUT_PERCENT(LAYOUT, COMPONENT)	((LAYOUT)->flags & (LAYOUT_FLAGS_##COMPONENT##_PERCENT))
struct cui_layout_st {
	char name[CUI_WIDGETTITLE_BUFLEN];
	float beginx;
	float beginy;
	float cols;
	float lines;
	struct list_head node;
	int flags;
};

struct cui_layoutgroup_st {
	char name[CUI_WIDGETTITLE_BUFLEN];
	struct list_head layoutlist;	// one layout has multiple layouts
	struct list_head node;
};

struct cui_label_st {
	char text[CUI_WIDGETTITLE_BUFLEN];
	int beginx;
	int beginy;
	int color;
};

struct cui_widget_operations {
	const char *name;
	int (*parse)(cJSON*, struct cui_widget_st*);	// cJSON, widget instance
	// int (*load)(struct cui_widget_st*, void*);
	int (*create)(struct cui_widget_st*);
	void (*destroy)(struct cui_widget_st*);
    void (*paint)(struct cui_widget_st*);
	// auto load initialize
	int (*initialize)(struct cui_widget_st*);
	// action/slot pairs
	int (*keyevent)(struct cui_widget_st*, int);
	int (*mouseevent)(struct cui_widget_st*, int);
	struct list_head node;
};

struct cui_brush_st {

};

struct cui_widget_st {
	WINDOW *win;
	int flags;		// CUI_WIDGET_FLAGS_INPUTWIDGET
	int beginx;		//topleft x relative to the global screen
	int beginy;		//topleft y relative to the global screen 
    int cols;
    int lines;
	struct cui_widget_border_st border;
	int id;			//window id, subwidget derived id from its root parent widget id. By checking parent == NULL to get to know whether it's a root window
	char name[CUI_WIDGETTITLE_BUFLEN];
	char title[CUI_WIDGETTITLE_BUFLEN];
	struct cui_widget_operations ops;
	struct cui_widget_event_callback cb;
	void *widgetinstance;	// point to cui_menu_st/cui_window_st
	void *priv;				// point to private data
	void *template;			// panel or form use 
	int nlabels;
	struct cui_label_st *clabel;
	struct cui_widget_st *parent;           // parent widget pointer
	struct cui_widget_st *sibling;			// sibling widget pointer
	struct cui_screen_st *cscr;

	struct list_head node;
	/*
		sub-widget node list head. 当widget是panel类型时，有2中模式，template和panel。当作为template时，widgetlist连接了所有子panel窗口，
		并且自身用于显示子panel窗口时的模板，不参与显示和事件处理。template的大小代表了所有子panel能显示的最大范围。
		作为子panel时，是众多panel窗口之一，参与显示和事件处理。
	*/
	struct list_head widgetlist;
};

struct cui_panel_st {
	PANEL *panel;
};

struct cui_menu_st {
	MENU *menu;
	ITEM **items;
	int nitems;
	char *name[CUI_MENUITEMS_MAX];
	char *desc[CUI_MENUITEMS_MAX];
	char mark[CUI_WIDGETMARK_BUFLEN];					// character for mark
	char title[CUI_WIDGETTITLE_BUFLEN];
	int mrows;	// mrows, mcols描述menu以几行几列形式显示菜单条目，通常1行多列
	int mcols;

	int beginy;
	int beginx;
	int lines;	// menu区域的显示范围
	int cols;
};

#define FIELD_FLAGS_BACKGROUND_UNDERLINE		0x1
#define FIELD_FLAGS_OPTIONS_AUTOSKIP			0x2


struct cui_field_st {
	char name[CUI_WIDGETTITLE_BUFLEN];	// field name
	int height;
	int width_field;
	int beginx_field;
	int beginy_field;
	int width_label;
	int beginx_label;
	int beginy_label;
	uint32_t options;
};

struct cui_form_st {
	int nfields;
	FIELD **fields;
	struct cui_field_st *cfields;
	FORM *form;
	WINDOW *win;
};

typedef int (*eventcb_function)(struct cui_widget_st*, int);

/* cui_debug.c */
void cui_dump_form(struct cui_widget_st *widget);
void cui_dump_screen(struct cui_screen_st *cscr);
void cui_dump_widget(struct cui_widget_st *widget);
void cui_dump_widget_hierarchy(struct cui_widget_st *widget);
void cui_dump_menu(struct cui_widget_st *widget);
const char *cui_key_str(int key);
const char* cui_strerror(int errnum);

/* cui_utils.c */
int is_string_equal_safe(const char *s1, const char *s2);
int is_string_equal_safe2(const char *s1, const char *s2, size_t n);
char *strncpy_safe(char *dest, const char *src, size_t n);
int cJSON_ValidArray(cJSON *node);
int cJSON_ValidNumber(cJSON *node);
int cJSON_ValidString(cJSON *node);
int is_float(const char *str);
int is_integer(const char *str);
void extract_filename(const char *filepath, char *filename);

/* cui_ops.c */
int register_widget_operation(struct cui_widget_operations *ops);
struct cui_widget_operations* get_widget_operation(const char *name);
void cui_initialize_operation(void);

/* cui_widget.c */
void cui_set_widget_layout(struct cui_widget_st* widget, const char *reference);
int cui_eventslots_parse(cJSON *attr, struct cui_widget_st* widget);
struct cui_widget_st* cui_get_default_inputwidget(struct cui_widget_st* widget);
int cui_ops_widget_recursive_depthfirst(struct list_head *widgetlist, int opstype);
int cui_ops_widget_recursive_widthfirst(struct list_head *widgetlist, int opstype);
int cui_install_initializer(struct cui_widget_st *widget);
int cui_set_widget_eventslot(struct cui_widget_st *widget, int eventtype, CUI_EVENT_CALLBACK cb);
int cui_widget_align(struct cui_widget_st *widget, int flags);
void cui_widget_resize(struct cui_widget_st *widget, int lines, int cols, int beginy, int beginx);

/* cui */
#define CUI_PRINT_ALIGNMENT_LEFT        0
#define CUI_PRINT_ALIGNMENT_MIDDLE      1
int cui_isrefreshed(struct cui_screen_st *cscr);
struct cui_screen_st* cui_load(const char* conf);
void cui_unload(struct cui_screen_st *cscr, int setendwin);
void cui_draw(struct cui_screen_st* cscr);
struct cui_widget_st* cui_new_widget(int id, struct cui_screen_st *cscr, struct cui_widget_operations *widgetops, struct cui_widget_st* parent, struct cui_widget_st* sibling);
struct cui_widget_st* cui_widget_dup(struct cui_widget_st* template);
void cui_widget_destroy(struct cui_widget_st *widget);
int cui_attributes_parse(cJSON *root, struct cui_widget_st* widget, struct cui_widget_operations *widgetops, void *widgetinstance);
void cui_print(WINDOW *win, int starty, int startx, int width, char *string, int color_foreback, int alignment);
void cui_print_autoreturn(struct cui_widget_st *widget, int starty, int startx, const char *string, int color);
int cui_autoreturn_rows(struct cui_widget_st *widget, int startx, const char *string);
void cui_eventloop(struct cui_screen_st* cscr);
void cui_default_operation_handler(struct cui_widget_operations *ops);
struct cui_widget_st* get_widget_byname(struct list_head *widgetlist, const char *name);
struct cui_widget_st* get_widget_byid(struct list_head *widgetlist, int id);
void cui_refresh(struct cui_screen_st *cscr, int cursor_visibility);
struct cui_widget_st* cui_screen_pop(struct cui_screen_st *cscr);
int cui_screen_push(struct cui_widget_st *widget, struct cui_widget_st *inputwidget);
struct cui_widget_st* cui_screen_peep(struct cui_screen_st *cscr);
void cui_initialize_screen(void);
void cui_set_widget_title(struct cui_widget_st *widget, const char *title);
cJSON* cui_guess_widgetnode(cJSON *nodewidget);
void cui_dump_widget_byname(struct cui_screen_st *cscr, const char *name);
int cui_layoutgrouplist_parse(cJSON *root, struct cui_screen_st *cscr);
void cui_dump_layout(struct list_head *layoutgrouplist);
void cui_update_inputwidget(struct cui_widget_st *widget);
void cui_print_left(WINDOW *win, int starty, int startx, int width, char *string);
void cui_trim_buf(char *buf);

/* cui_menu.c */
int cui_menu_initialize(struct cui_widget_st *widget, int nitems, const char **item_namelist, const char **item_desclist, int current_item_index);
int cui_menu_mark(struct cui_widget_st *widget, const char *mark);
void cui_menu_resize(struct cui_widget_st *widget, int lines, int cols, int beginy, int beginx, int mrows, int mcols);
void cui_menu_align(struct cui_widget_st *widget, int flags);

/* cui window operation */
WINDOW* cui_window_getwin(struct cui_widget_st* widget);
int cui_window_getkey(struct cui_widget_st* widget, int *c);
void cui_window_paint(struct cui_widget_st *widget);
void cui_window_destroy(struct cui_widget_st *widget);
void cui_set_border_highlight(struct cui_widget_st* widget, int isset);

/* cui_panel.c */
int cui_panel_duplicate(struct cui_widget_st* template, int ncopies);
int cui_panel_rearrange(struct cui_widget_st* template, int rearrange);
void cui_panel_reset_template(struct cui_widget_st* template);
void cui_text_flushchar(struct cui_widget_st *widget, int index, char flush_char);
int cui_label_printf(struct cui_widget_st *widget, int index, const char* stream, ...);

/* cui_text.c */
int cui_label_parse(cJSON *attr, struct cui_widget_st* widget);

/* cui_form.c */
#define CUI_WIDGET_ALIGNMENT_XMID       0x1
#define CUI_WIDGET_ALIGNMENT_YMID       0x2

#define CUI_WIDGET_ALIGNMENT_XYMID		(CUI_WIDGET_ALIGNMENT_XMID | CUI_WIDGET_ALIGNMENT_YMID)

int cui_form_initialize(struct cui_widget_st *widget, int nitems, const char **name, struct cui_field_st *template);
void cui_form_destroy(struct cui_widget_st *widget);
void cui_form_reset(struct cui_widget_st *widget);
int cui_form_keyevent(struct cui_widget_st* widget, int key);
int cui_form_align(struct cui_widget_st *widget, int flags);
void cui_initialize_dummy_field(struct cui_field_st *cfield);
int cui_field_setvalue(struct cui_widget_st *widget, int fieldno, const char* value);
void cui_field_setoptions(struct cui_widget_st *widget, int fieldno, int options, int on);
void cui_field_setfocus(struct cui_widget_st *widget, int fieldno);
#endif // !__CUI_H__
